package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"github.com/hyperledger/fabric/protos/peer"
	"strconv"
	"time"
)

const (
	TransactionKeyPrefix  = "transaction"
	TxStatusNew           = "新建（待确认）"
	TxStatusConfirm       = "新建（已确认）"
	TxStatusPayForAnother = "申请代付"
	TxStatusPay           = "已付款（未核实）"
	TxStatusPayed         = "已付款（已核实）"
	TxStatusRefusePay     = "代付被拒"
	TxStatusShip          = "已发货（未核实）"
	TxstatusShiped        = "已发货（已核实）"
	TxStatusRepay         = "已还款（未核实）"
	TxStatusRepayed       = "已还款（已核实）"
)

type SupplyChainFinance struct {
}

// 	新建 -> 已确认 -> 申请代付 -> 已付款 ->确认付款-> 已发货 -> 确认发货 -> 已还款 -> 确认已还款
//                       |
//                    拒绝付款 ->回到上一步
type Transaction struct {
	Id                string    `json:"id"`                  // 单号
	Description       string    `json:"description"`         // 备注
	Value             uint64    `json:"value"`               // 金额(单位为一分钱)
	ShipmentNumber    string    `json:"shipment_number"`     // 物流号
	PaymentTxNumber   string    `json:"payment_tx_number"`   // 付款转账号
	RepaymentTxNumber string    `json:"repayment_tx_number"` // 还款转账号
	Status            string    `json:"status"`              // 订单状态      ----请使用上面const中的常量来标记订单状态
	CreateDate        time.Time `json:"create_date"`         // 创建时间
	PaymentDate       time.Time `json:"payment_date"`        // 付款时间
	RepaymentDate     time.Time `json:"repayment_date"`      // 应还款时间
	RepayDate         time.Time `json:"repay_date"`          // 实际还款时间
	Supplier          string    `json:"supplier"`            // 供应商
	Company           string    `json:"company"`             // 采购商
	FinancialOrg      string    `json:"financial_org"`       // 金融机构
}

type Company struct {
	Name           string    `json:"name"`            //企业名
	Phone          string    `json:"phone"`           //联系电话
	Account        string    `json:"account"`         //登录账号
	Password       string    `json:"password"`        //登录密码
	Address        string    `json:"address"`         //地址
	CompanyType    string    `json:"company_type"`    //企业类型，供应商，采购商，金融机构
	EnterpriseCode string    `json:"enterprise_code"` //企业统一社会信用代码
	BankCard       string    `json:"bank_card"`       //银行卡号码
	Balance        uint64    `json:"balance"`         //余额
	CreateDate     time.Time `json:"create_date"`     //创建时间
}

// 企业的3种类型
var companyArg = map[string]string{"financial_org": "financial_org", "supplier": "supplier", "buyer": "buyer"}

/**
  @Author: dd
  @Date:   2020/5/13 16:22
  @Desc:   智能合约初始化，要求必须创建3个企业，分别为供应商、采购商、金融企业 ,Company.Name="datainsights" （必需） *
  @Param:
  @Return:
**/
func (s *SupplyChainFinance) Init(stub shim.ChaincodeStubInterface) peer.Response {
	//fmt.Println("init chaincode")
	return shim.Success(nil)
}

func (s *SupplyChainFinance) Invoke(stub shim.ChaincodeStubInterface) peer.Response {
	funcName, args := stub.GetFunctionAndParameters()

	switch funcName {
	case "newTransaction":
		return newTransaction(stub, args)
	case "createCompany":
		return createCompany(stub, args)
	case "confirmTransaction":
		return confirmTransaction(stub, args)
	case "applyPayForAnother":
		return applyPayForAnother(stub, args)
	case "payTransaction":
		return payTransaction(stub, args)
	case "affirmPay":
		return affirmPay(stub, args)
	case "refusePayTransaction":
		return refusePayTransaction(stub, args)
	case "updateShipmentInfo":
		return updateShipmentInfo(stub, args)
	case "verifyShipmentInfo":
		return verifyShipmentInfo(stub, args)
	case "repayTransaction":
		return repayTransaction(stub, args)
	case "verifyRepay":
		return verifyRepay(stub, args)
	case "autoRepay":
		return autoRepay(stub, args)
	case "getTransactions":
		return getTransactions(stub, args)
	case "getTrByBuyer":
		return getTrByBuyer(stub, args)
	case "getTransactionHistory":
		return getTransactionHistory(stub, args)
	case "getCompany":
		return getCompany(stub, args)
	default:
		return shim.Error(fmt.Sprintf("unsupported function: %s", funcName))
	}
}

/**
  @Author: dd
  @Date:   2020/5/13 16:48
  @Desc:   根据company.CompanyType来创建3种不同的企业*
  @Param:  Company
  @Return: res = peer.Response,       要求返回结果时能够返回正确相应的响应码：shim.ERROR,shim.ERRORTHRESHOLD,shim.OK
**/
func createCompany(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 18:01
  @Desc:   创建新的交易 *
  @Param:  {id:string, Value:int, supplier:string, create_date:time.Time, company:string}
  @Return:
**/
func newTransaction(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 18:00
  @Desc:   供应商确认交易 *
  @Param:  {id:string, supplier:string}
  @Return:
**/
func confirmTransaction(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:57
  @Desc:   向金融机构申请代付 *
  @Param:  {id:string, company:string, financial_org:string, repayment_date:time.Time}
  @Return:
**/
func applyPayForAnother(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:57
  @Desc:   付款  *
  @Param:  {id:string,PaymentTxNumber:string,financial_org:string,payment_date:time.Time}
  @Return:
**/
func payTransaction(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 18:00
  @Desc:   供应商核实付款 *
  @Param:  {id:string, supplier:string}
  @Return:
**/
func affirmPay(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:55
  @Desc:   拒绝付款要求  *
  @Param:  {id:string,description:string,financial_org:string}
  @Return:
**/
func refusePayTransaction(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:54
  @Desc:   供应商发货  *
  @Param:  {id:string,shipment_number:string,supplier:string}
  @Return:
**/
func updateShipmentInfo(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:53
  @Desc:   买方收货  *
  @Param:  {id:string,company:string}
  @Return:
**/
func verifyShipmentInfo(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:50
  @Desc:   买方还款  *
  @Param:  {id:string,repayment_tx_number:string,tx_to_update:time.Time,company:string}
  @Return:
**/
func repayTransaction(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:48
  @Desc:   金融机构确认还款  *
  @Param:  {id:string,financial_org:string}
  @Return:
**/
func verifyRepay(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

func autoRepay(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/14 17:40
  @Desc:   验证公司是否存在 *
  @Param:  companyType:string, name:string
  @Return: bool，存在返回true，不存在返回false
**/
func valideCompany(stub shim.ChaincodeStubInterface, companyType, name string) bool {
	return false
}

/**
  @Author: dd
  @Date:   2020/5/14 17:41
  @Desc:   转账 从out转到in  *
  @Param:
  @Return:
**/
func transferAccounts(stub shim.ChaincodeStubInterface, out, outType, in, inType string, number uint64) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:45
  @Desc:   根据企业类型获取企业信息 *
  @Param:  companyType:string 企业类型
  @Return: companys:[]Company
**/
func getCompany(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 17:37
  @Desc:   获取买方的交易信息 *
  @Param:  buyer 买方公司名
  @Return: {transactions:[]Transaction,unfinished:int,total:int,violateTreaty:int}
			分别为此买方的交易信息、未完成交易数、交易总数、违约交易数（实际还款时间RepayDate大于应还款时间RepaymentDate）z
**/
func getTrByBuyer(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	return shim.Success(nil)
}

/**
  @Author: dd
  @Date:   2020/5/15 18:04
  @Desc:   获取交易信息  根据Id查询交易，不传数组则查询所有
  @Param:  [id0,id1] []string
  @Return: transactions []Transaction
**/
func getTransactions(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	// 检查参数的个数
	if len(args) > 1 {
		return shim.Error("invalid args.")
	}
	// 所有要查询的key，如果为空则查询所有
	var keys []string

	// 验证参数的正确性
	if len(args) == 1 {
		// 将第一个参数取出来反序列化为[]string
		var argKeys []string
		if err := json.Unmarshal([]byte(args[0]), &argKeys); err != nil {
			return shim.Error(fmt.Sprintf("unmarshal error: %s", err))
		}

		keys = append(keys, argKeys...)
	}

	var transactions []Transaction
	if len(keys) == 0 {
		// 传入的keys长度为0，则查找并返回所有数据
		// 通过主键从区块链查找相关的数据
		resultIterator, err := stub.GetStateByPartialCompositeKey(TransactionKeyPrefix, keys)
		if err != nil {
			return shim.Error(fmt.Sprintf("query transactions error: %s", err))
		}
		defer resultIterator.Close()

		// 检查返回的数据是否为空，不为空则遍历数据，否则返回空数组
		for resultIterator.HasNext() {
			val, err := resultIterator.Next()
			if err != nil {
				return shim.Error(fmt.Sprintf("resultIterator error: %s", err))
			}

			var transaction Transaction
			if err := json.Unmarshal(val.GetValue(), &transaction); err != nil {
				return shim.Error(fmt.Sprintf("unmarshal error: %s", err))
			}

			transactions = append(transactions, transaction)
		}
	} else {
		// 传入的keys长度不为0，查找相应的数据并返回
		for _, v := range keys {
			// 创建组合键
			key, err := stub.CreateCompositeKey(TransactionKeyPrefix, []string{v})
			if err != nil {
				return shim.Error(fmt.Sprintf("CreateCompositeKey error: %s", err))
			}

			// 从账本中获取数据
			partBytes, err := stub.GetState(key)
			if err != nil {
				return shim.Error(fmt.Sprintf("GetState error: %s", err))
			}

			if len(partBytes) != 0 {
				// 反序列化数据
				var transaction Transaction
				err = json.Unmarshal(partBytes, &transaction)
				if err != nil {
					return shim.Error(fmt.Sprintf("Unmarshal error: %s", err))
				}

				// 追加进结果数组
				transactions = append(transactions, transaction)
			}
		}
	}

	//序列化数据
	result, err := json.Marshal(transactions)
	if err != nil {
		return shim.Error(fmt.Sprintf("marshal error: %s", err))
	}

	return shim.Success(result)
}

/**
  @Author: dd
  @Date:   2020/5/15 18:07
  @Desc:   查询此交易的历史信息
  @Param:  id:string
  @Return:
**/
func getTransactionHistory(stub shim.ChaincodeStubInterface, args []string) peer.Response {
	// 检查参数的个数
	if len(args) != 1 {
		return shim.Error("只需要一个参数：订单id")
	}

	// 创建组合键
	compositeKey, err := stub.CreateCompositeKey(TransactionKeyPrefix, args)
	if err != nil {
		return shim.Error(fmt.Sprintf("CreateCompositeKey error: %s", err))
	}

	// 获取该key的历史，返回一个迭代器
	resultIterator, err := stub.GetHistoryForKey(compositeKey)
	if err != nil {
		return shim.Error(fmt.Sprintf("GetHistoryForKey error: %s", err))
	}
	defer resultIterator.Close()

	// buffer是一个包含了订单历史记录的JSON数组
	var buffer bytes.Buffer
	buffer.WriteString("[")

	bArrayMemberAlreadyWritten := false
	// 遍历迭代器，将值取出来
	for resultIterator.HasNext() {
		response, err := resultIterator.Next()
		if err != nil {
			return shim.Error(err.Error())
		}
		// 在数组元素前加",",第一个元素前不加
		if bArrayMemberAlreadyWritten == true {
			buffer.WriteString(",")
		}
		buffer.WriteString("{\"TxId\":")
		buffer.WriteString("\"")
		buffer.WriteString(response.TxId)
		buffer.WriteString("\"")

		buffer.WriteString(", \"Value\":")
		// 如果该记录的操作是删除，则返回null，否则返回数据的值
		if response.IsDelete {
			buffer.WriteString("null")
		} else {
			buffer.WriteString(string(response.Value))
		}

		buffer.WriteString(", \"Timestamp\":")
		buffer.WriteString("\"")
		buffer.WriteString(time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)).String())
		buffer.WriteString("\"")

		buffer.WriteString(", \"IsDelete\":")
		buffer.WriteString("\"")
		buffer.WriteString(strconv.FormatBool(response.IsDelete))
		buffer.WriteString("\"")

		buffer.WriteString("}")
		bArrayMemberAlreadyWritten = true
	}

	buffer.WriteString("]")

	fmt.Printf("getHistoryForPatent returning:\n%s\n", buffer.String())

	// 返回结果
	return shim.Success(buffer.Bytes())
}

func main() {
	err := shim.Start(new(SupplyChainFinance))
	if err != nil {
		fmt.Printf("Error starting chaincode: %s", err)
	}
}
